2023年最新Ajax爬取技术教程
前言
有没有遇到过这样的情况:用 requests 直接请求某个网页(比如微博、抖音网页版或者小红书旧版列表),拿到的 HTML 只有框架,正文、列表数据全是空的?这大概率是因为网站用了 Ajax(异步JavaScript和XML) 动态加载内容——不是服务器一开始就吐给你全量数据,而是页面加载后,前端再偷偷请求后端接口,拿到JSON/XML再渲染到页面上。
这篇教程就带你从「开发者工具入门抓包」到「实战微博移动端」,覆盖2023-2024主流的Ajax分析、反爬应对基础,全程无复杂公式,30分钟内就能上手!
1. 现代Ajax请求分析技术
Ajax的核心本质是「前端异步请求后端接口拿数据」,所以我们的第一步就是找到这个隐藏的接口——靠的就是所有现代浏览器(Chrome、Edge、Firefox都可以,推荐Chrome)自带的「开发者工具」。
1.1 开发者工具快速开启
不用再记繁琐的右键菜单顺序,这几个组合键一键搞定:
- Windows/Linux:
F12 或 Ctrl + Shift + I
- macOS:
Cmd + Option + I
✅ 专业高效操作:
- 先打开目标页面(比如微博移动端:
https://m.weibo.cn/u/2830678474)
- 按快捷键打开工具→切换到顶部的 Network(网络) 选项卡
- 按
Ctrl + R(Windows/Linux)或 Cmd + R(macOS)强制刷新页面——这样才能完整捕获所有请求,包括最初就触发的静态资源和动态接口。
1.2 快速筛选隐藏的Ajax接口
现在Network面板里全是密密麻麻的请求(CSS、JS、图片、字体……),直接找接口太麻烦,用这3个筛选标签精准定位:
- Fetch/XHR:覆盖99%的现代动态接口(包括老的
XMLHttpRequest 和新的 fetch API)
- WS:如果是实时聊天、直播弹幕这类「双向实时通信」的内容,找这个标签(WebSocket)
- GraphQL:部分新网站(比如GitHub新版部分页面、Notion)会用这个,筛选栏可能需要手动点击「Filter」→ 勾选「GraphQL」
筛选完Fetch/XHR后,剩下的请求基本都是我们要找的动态接口了!
1.3 快速判断接口是否有用
找到了一堆接口,怎么知道哪个是「正文列表」「用户信息」的接口?可以用这3个小技巧:
- 看请求方法和URL后缀:
- 大部分数据接口用
GET(获取)或 POST(提交参数较多的复杂请求)
- URL通常会带
/api/ /v2/ /feed/ /list/ /user/ 这类关键词
- 看响应预览(Preview):
在Network面板点击某个请求→切换到右侧的 Preview 标签——如果看到了熟悉的内容(比如刚才打开的微博的正文、用户头像URL),那恭喜你,找到了目标接口!
- 复制curl调试逻辑:
怕自己漏看请求头?右键点击有用的请求→Copy → Copy as cURL (bash),可以拿到和浏览器完全一致的请求参数,后面转成Python代码也超级方便。
2. 2023-2024主流反爬应对基础
找到接口只是第一步,很多网站会加上反爬机制——直接复制接口URL用浏览器打开可能没问题,但用Python请求就403/401/返回空数据。这里给大家几个入门但实用的解决方案:
2.1 完整模拟浏览器请求(最常用)
大部分入门级反爬(比如检查User-Agent、Referer、Cookie这些请求头),只要把刚才Copy的cURL转成Python代码就能解决!
推荐用这个免费工具一键转换:curlconverter.com
⚠️ 注意事项:
- 如果有Cookie的话,转成代码后不要硬写长期有效的Cookie(容易过期/被封号),可以用
httpx 的 cookiejar 管理
- 加上
http2=True 参数——很多新网站已经强制用HTTP/2了,不加可能会403
这里给大家贴一个通用的完整模拟请求模板(用了支持HTTP/2的异步库 httpx,比 requests 快很多):
import httpx
async def fetch_api_data(url: str, method: str = "GET", params: dict = None, headers: dict = None, data: dict = None):
"""
通用异步模拟请求函数
:param url: 目标接口URL
:param method: 请求方法(GET/POST)
:param params: GET请求的查询参数
:param headers: 完整请求头(建议从curlconverter复制)
:param data: POST请求的表单数据
"""
async with httpx.AsyncClient(http2=True, follow_redirects=True) as client:
try:
resp = await client.request(
method=method,
url=url,
params=params,
headers=headers,
data=data,
timeout=10.0 # 超时设置,避免卡死
)
resp.raise_for_status() # 自动抛出4xx/5xx错误
return resp.json() # 大部分接口返回JSON,直接解析
except httpx.HTTPStatusError as e:
print(f"请求失败,状态码:{e.response.status_code}")
except Exception as e:
print(f"其他错误:{e}")
2.2 应对动态参数的入门方法
如果模拟完整请求后还是不行,大概率是接口有动态参数(比如 sign token _t 这类每次请求都变的参数)。
入门级的动态参数可以用 「PyExecJS直接执行前端加密JS」 解决:
- 在开发者工具的 Sources(源代码) 标签里,找到生成动态参数的JS函数(可以用
Ctrl + Shift + F 全局搜索参数名,比如 sign)
- 把这段JS函数复制出来,注意补全它依赖的其他变量/函数
- 用PyExecJS执行这段JS,拿到动态参数
举个简单的例子(假设生成 token 的函数是 getToken(timestamp)):
import execjs
import time
# 补全依赖后的JS代码
js_code = """
// 假设这是从Sources里找到的生成token的函数
function getRandomStr() {
return Math.random().toString(36).substr(2, 9);
}
function getToken(t) {
return getRandomStr() + t.toString().substr(-6);
}
"""
# 编译JS代码
ctx = execjs.compile(js_code)
# 调用函数,传入当前时间戳(单位:秒)
timestamp = int(time.time())
token = ctx.call("getToken", timestamp)
print(f"生成的动态token:{token}")
3. 微博移动端实战(2023年12月亲测有效)
刚才讲了这么多理论,现在我们用 微博移动端某博主的主页(https://m.weibo.cn/u/2830678474,随便选的一个公开博主,不涉及隐私)来实战!
3.1 抓包找到目标接口
按照1.1-1.3的步骤操作:
- 打开目标页面→打开Network→筛选Fetch/XHR→强制刷新
- 点击几个请求的Preview,发现
/api/feed/profile 返回了博主的微博列表HTML片段(没错,有些接口虽然带api,但返回的是HTML而不是纯JSON)
- 切换到 Headers 标签,找到请求的URL、查询参数、请求头
目标接口的关键信息:
- 请求方法:
GET
- URL:
https://m.weibo.cn/api/feed/profile
- 查询参数:
uid(博主ID,必填)、page(页码,从1开始)
- 必要请求头:
User-Agent(移动端的)、Referer(博主主页的URL)、X-Requested-With(XMLHttpRequest,标识Ajax请求)
3.2 完整Python实现代码
结合刚才的通用模板和抓包结果,加上用 parsel 解析HTML片段的逻辑:
import httpx
from parsel import Selector
import asyncio
# ---------------------- 配置参数 ----------------------
UID = "2830678474" # 目标博主ID
MAX_PAGE = 2 # 爬取的最大页数
HEADERS = {
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1",
"X-Requested-With": "XMLHttpRequest",
"Referer": f"https://m.weibo.cn/u/{UID}",
}
# ---------------------- 核心函数 ----------------------
async def fetch_weibo_page(uid: str, page: int = 1):
"""爬取单页微博"""
async with httpx.AsyncClient(http2=True, follow_redirects=True) as client:
try:
resp = await client.get(
url="https://m.weibo.cn/api/feed/profile",
params={"uid": uid, "page": page},
headers=HEADERS,
timeout=10.0
)
resp.raise_for_status()
return resp.json()
except Exception as e:
print(f"第{page}页爬取失败:{e}")
return None
def parse_weibo_html(html: str):
"""解析微博HTML片段,提取有用信息"""
selector = Selector(text=html)
weibo_list = []
# 遍历每个微博卡片
for card in selector.css(".card-wrap"):
# 过滤掉广告卡片(带mblog-tag--ad的类)
if card.css(".mblog-tag--ad"):
continue
# 提取信息
weibo_list.append({
"微博ID": card.css(".card::attr(mid)").get(),
"发布时间": card.css(".time::text").get(),
"微博正文": "".join(card.css(".weibo-text::text, .weibo-text a::text").getall()).strip(),
"点赞数": card.css(".like-count::text").get("0"),
"评论数": card.css(".comment-count::text").get("0"),
"转发数": card.css(".repost-count::text").get("0"),
})
return weibo_list
async def main():
"""主函数:多页爬取"""
all_weibos = []
for page in range(1, MAX_PAGE + 1):
print(f"正在爬取第{page}页...")
data = await fetch_weibo_page(UID, page)
if not data or not data.get("data", {}).get("cards"):
print(f"第{page}页没有数据,停止爬取")
break
# 注意:2023年12月的接口返回的cards里,第一个是置顶微博(带isTop:1),后面是普通微博
# 这里直接取所有cards的html片段拼接起来解析
full_html = "".join([card.get("mblog", {}).get("text", "") for card in data["data"]["cards"] if card.get("mblog")])
page_weibos = parse_weibo_html(full_html)
all_weibos.extend(page_weibos)
# 加个3秒的延迟,避免被封
await asyncio.sleep(3)
# 打印结果
print(f"\n爬取完成,共获取{len(all_weibos)}条有效微博:")
for idx, weibo in enumerate(all_weibos, 1):
print(f"\n【第{idx}条】")
for key, value in weibo.items():
print(f"{key}:{value}")
if __name__ == "__main__":
asyncio.run(main())
4. 法律与道德红线(必须看!)
技术是中立的,但使用技术的人必须遵守规则,否则可能会面临法律风险:
- 遵守robots.txt:访问目标网站的
https://域名/robots.txt,看看有没有禁止爬取的路径
- 设置合理的爬取间隔:建议至少
3秒/请求,不要给目标服务器造成压力
- 不爬取个人隐私数据:比如手机号、身份证号、私密朋友圈/微博
- 遵守法律法规:比如《数据安全法》《个人信息保护法》《网络安全法》
总结
这篇教程带你从「抓包入门」到「实战微博」,掌握了现代Ajax爬取的基础逻辑:
- 用开发者工具找到隐藏的Ajax接口(筛选Fetch/XHR→看Preview)
- 完整模拟浏览器请求(用curlconverter转代码→加http2=True)
- 入门级动态参数应对(用PyExecJS执行前端JS)
- 遵守法律与道德红线
如果遇到更复杂的反爬(比如TLS指纹、WebAssembly加密、行为验证),可以关注后续的进阶教程!